import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';

class MyListener extends SingleChildRenderObjectWidget {
  /// Creates a widget that forwards point events to callbacks.
  ///
  /// The [behavior] argument defaults to [HitTestBehavior.deferToChild].
  const MyListener({
    super.key,
    this.onPointerDown,
    this.onPointerUp,
    this.onPointerMove,
    this.behavior = HitTestBehavior.deferToChild,
    super.child,
  });

  /// Called when a pointer comes into contact with the screen (for touch
  /// pointers), or has its button pressed (for mouse pointers) at this widget's
  /// location.
  final PointerDownEventListener? onPointerDown;

  /// Called when a pointer that triggered an [onPointerDown] is no longer in
  /// contact with the screen.
  final PointerUpEventListener? onPointerUp;

  /// Called when a pointer that triggered an [onPointerDown] changes position.
  final PointerMoveEventListener? onPointerMove;

  /// How to behave during hit testing.
  final HitTestBehavior behavior;

  @override
  MyRenderPointerListener createRenderObject(BuildContext context) {
    return MyRenderPointerListener(
      onPointerDown: onPointerDown,
      onPointerUp: onPointerUp,
      onPointerMove: onPointerMove,
    );
  }
}

class MyRenderPointerListener extends RenderProxyBoxWithHitTestBehavior {
  /// Creates a render object that forwards pointer events to callbacks.
  ///
  /// The [behavior] argument defaults to [HitTestBehavior.deferToChild].
  MyRenderPointerListener({
    this.onPointerDown,
    this.onPointerUp,
    this.onPointerMove,
    super.child,
  });

  /// Called when a pointer comes into contact with the screen (for touch
  /// pointers), or has its button pressed (for mouse pointers) at this widget's
  /// location.
  PointerDownEventListener? onPointerDown;

  /// Called when a pointer that triggered an [onPointerDown] is no longer in
  /// contact with the screen.
  PointerUpEventListener? onPointerUp;

  /// Called when a pointer that triggered an [onPointerDown] changes position.
  PointerMoveEventListener? onPointerMove;

  @override
  Size computeSizeForNoChild(BoxConstraints constraints) {
    return constraints.biggest;
  }

  @override
  void handleEvent(PointerEvent event, HitTestEntry entry) {
    assert(debugHandleEvent(event, entry));
    if (event is PointerDownEvent) {
      return onPointerDown?.call(event);
    }
    if (event is PointerUpEvent) {
      return onPointerUp?.call(event);
    }
    if (event is PointerMoveEvent) {
      return onPointerMove?.call(event);
    }
  }
}
